package com.df.my_net_game.game_rpc.config;

import com.df.my_net_game.game_rpc.client.annotation.GRpc;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.util.Map;
import java.util.Set;

public class GRpcClientRegistry implements ImportBeanDefinitionRegistrar, ResourceLoaderAware , EnvironmentAware {
    ResourceLoader resourceLoader;
    Environment environment;
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        String packageName = ClassUtils.getPackageName(importingClassMetadata.getClassName());
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry){
            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().hasAnnotation(GRpc.class.getName());
            }
        };
        scanner.setResourceLoader(resourceLoader);
        scanner.addIncludeFilter(new AnnotationTypeFilter(GRpc.class));
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(packageName);
        for (BeanDefinition candidateComponent : candidateComponents) {
            if(candidateComponent instanceof AnnotatedBeanDefinition){
                AnnotatedBeanDefinition beanDefinition = (AnnotatedBeanDefinition) candidateComponent;
                AnnotationMetadata metadata = beanDefinition.getMetadata();
                Assert.isTrue(metadata.isIndependent(), "@GRpc 只能使用在接口上！");
                Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(GRpc.class.getCanonicalName());
                registryGameRpcClient(registry,metadata,annotationAttributes);
            }
        }
    }

    private void registryGameRpcClient(BeanDefinitionRegistry registry, AnnotationMetadata metadata, Map<String, Object> annotationAttributes) {
        String className = metadata.getClassName();
        Class<?> aClass = ClassUtils.resolveClassName(metadata.getClassName(), null);
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(GRpcClientFactoryBean.class);
        Object o = annotationAttributes.get("server");
        if(o ==null){
            throw new RuntimeException(String.format("class [%s] ,@GRpc 的server属性不能为null或空！",className));
        }
        String server = o.toString();
        if(server.isBlank()){
            throw new RuntimeException(String.format("class [%s] ,@GRpc 的server属性不能为null或空！",className));
        }
        builder.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        AbstractBeanDefinition beanDefinition = builder.getBeanDefinition();
        beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(aClass);
        BeanDefinitionHolder holder = new BeanDefinitionHolder(beanDefinition,className,new String[]{server+"GRpcClient"});
        BeanDefinitionReaderUtils.registerBeanDefinition(holder, registry);
    }



    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }
}
